#include "../hle.h"

#include "__ThreadManForUser.h"

s32 interruptsEnabled = 1;

void sceKernelCpuSuspendIntr()
{
    s32 value = 0x1;
    _log(TRC, HLE, "0x%03x (hack) = sceKernelCpuSuspendIntr()", value);
    interruptsEnabled = 0;
    RETURN(value); /* TODO: this is a random value, just for debug */
}

void sceKernelCpuResumeIntr()
{
    _log(TRC, HLE, "sceKernelCpuResumeIntr(%08x)", PARAM(0));
    interruptsEnabled = 1;
    RETURN(0);
}

void sceKernelCpuResumeIntrWithSync()
{
    _log(ERR, HLE, "sceKernelCpuResumeIntrWithSync");
    RETURN(0);
}

void sceKernelIsCpuIntrEnable()
{
    u32 retVal = (u32)interruptsEnabled; 
    _log(ERR, HLE, "%d = sceKernelIsCpuIntrEnable()", retVal);
    RETURN(retVal);
}

void sceKernelMemset()
{
    u32 i;
    u32 addr = PARAM(0);
    u8 c = PARAM(1) & 0xff;
    u32 n = PARAM(2);
    _log(INF, HLE, "sceKernelMemset(ptr = %08x, c = %02x, n = %08x)", addr, c, n);
    for (i = 0; i < n; i++)
        mem_write8(addr + i, c);
    RETURN(addr); /* TODO: verify it should return this */
}

void sceKernelLockLwMutex()
{
    u32 idAddr = PARAM(0);
    SceUID id = mem_read32(idAddr);
    u32 count = PARAM(1);
    u32 timeoutAddr = PARAM(2);
    u32 timeout;
    LwMutex *lwmtx = ko_get(id);
    if (mem_isOk(timeoutAddr))
        timeout = mem_read32(timeoutAddr);
    else
        timeout = 0;
    if (lwmtx)
    {
        if (lwmtx->n.curCount == 0) {
            lwmtx->n.curCount += count;
            lwmtx->thread = __KernelGetCurThread();
        }
        else if (lwmtx->n.attr & LWMUTEX_ATTR_RECURSIVE && lwmtx->thread == __KernelGetCurThread())
            lwmtx->n.curCount += count;
        else {
           lwmtx->waitingThreads[lwmtx->n.numWaitThreads++] = __KernelGetCurThread();
            __KernelWaitCurThread(WAITTYPE_LWMUTEX, id, 0, timeout, 0);
        }
        _log(INF, HLE, "0 = sceKernelLockLwMutex(id = %08x [%d], count = %d, timeoutAddr = %08x)", idAddr, id, count, timeoutAddr);
        RETURN(0);
    }    
    else {  
        _log(ERR, HLE, "sceKernelLockLwMutex(id = %08x [%d], count = %d, timeoutAddr = %08x): lwmutex not found", idAddr, id, count, timeoutAddr);
        RETURN(SCE_ERROR_LWMUTEX_NOT_FOUND);
    }
}

void sceKernelUnlockLwMutex()
{
    u32 idAddr = PARAM(0);
    SceUID id = mem_read32(idAddr);
    u32 count = PARAM(1);
    LwMutex *lwmtx = ko_get(id);
    if (lwmtx)
    {       
        if ((s32)lwmtx->n.curCount - (s32)count >= 0)
        {  
            u32 oldval = lwmtx->n.curCount;
            lwmtx->n.curCount -= count;
            if (lwmtx->n.curCount == 0 && lwmtx->n.numWaitThreads != 0)
            {
                /* TODO: use attribute to know which lwmutex to restart */
                u32 i;
                SceUID id;

                id = lwmtx->waitingThreads[0];
                __KernelResumeThread(id);

                lwmtx->n.numWaitThreads--;
                for (i = 0; i < lwmtx->n.numWaitThreads; i++)
                    lwmtx->waitingThreads[i] = lwmtx->waitingThreads[i + 1];
                __KernelReSchedule();
            }
            _log(INF, HLE, "sceKernelUnlockLwMutex(id = %08x [%d], count = %d) [old: %d, new: %d]", idAddr, id, count, oldval, lwmtx->n.curCount);
            RETURN(0);
        }   
        else if (lwmtx->n.curCount != 0) {
            _log(ERR, HLE, "sceKernelUnlockLwMutex(id = %08x [%d], count = %d): lwmutex underflow", idAddr, id, count);
            RETURN(SCE_ERROR_LWMUTEX_UNLOCK_UNDERFLOW);
        }   
        else {
            _log(ERR, HLE, "sceKernelUnlockLwMutex(id = %08x [%d], count = %d): lwmutex already unlocked", idAddr, id, count);
            RETURN(SCE_ERROR_LWMUTEX_UNLOCKED);
        }
    }     
    else {
        _log(ERR, HLE, "sceKernelUnlockLwMutex(id = %08x [%d], count = %d): lwmutex not found", idAddr, id, count);
        RETURN(SCE_ERROR_LWMUTEX_NOT_FOUND);
    }
}

